package sqlite

import (
	"database/sql"
	"runtime"

	"gitee.com/yrwy/msgo/pkg/storage"
	_ "github.com/logoove/sqlite"
	//_ "github.com/mattn/go-sqlite3"
)

type Store struct {
	db      *sql.DB
	st      *sql.Stmt
	prepare string
}

type Option func(*Store) error

func WithExec(sql string, args ...any) Option {
	return func(x *Store) error {
		_, err := x.db.Exec(sql, args...)
		return err
	}
}

func NewStore(db *sql.DB, prepare string, opts ...Option) (*Store, error) {
	r := &Store{db: db}
	for _, op := range opts {
		err := op(r)
		if err != nil {
			return nil, err
		}
	}
	r.prepare = prepare
	st, err := db.Prepare(prepare)
	if err != nil {
		return nil, err
	}
	runtime.SetFinalizer(st, func(o *sql.Stmt) {
		o.Close()
	})
	r.st = st
	return r, nil
}

func NewFile(filename string, prepare string, opts ...Option) (*Store, error) {
	db, err := sql.Open("sqlite3", filename)
	if err != nil {
		return nil, err
	}
	runtime.SetFinalizer(db, func(o *sql.DB) {
		o.Close()
	})
	return NewStore(db, prepare, opts...)
}

func (x *Store) Write(d storage.IData) error {
	row := d.GetRow()
	_, err := x.st.Exec(row...)
	return err
}

func (x *Store) Writes(ds []storage.IData) (int, error) {
	tx, err := x.db.Begin()
	if err != nil {
		return 0, err
	}
	st, err := tx.Prepare(x.prepare)
	if err != nil {
		return 0, err
	}
	defer st.Close()
	defer tx.Commit()
	for i, d := range ds {
		row := d.GetRow()
		_, err := st.Exec(row...)
		if err != nil {
			return i, err
		}
	}
	return len(ds), nil
}
